www.gusucode.com > C++ 多线程TELNET服务程序 > C++ 多线程TELNET服务程序/gusucode/server.cpp
//Download by http://www.NewXing.com // Copyright (c) 1999 Lee Patterson // leepatterson@home.com // // Demonstrats: // // o Internet Server that accepts telnet connections. // o Using multiple threads to handle more then one connection. // o BlockingSocket class for using sockets. // (I'd recomend this class over the MS CSocket and such, as they still have // some 16-bit stuff in them, and this class ports to Unix a lot easier. // That was the main goal of my program.) // // Also included: // // o Simple linked list class that can be used anywhere (unix/mfc/console). // (Note: The linked list class is not thread safe.) // // I'd be happy to field any questions about the workings // of the server, or how I set things up, but I'm not interested in fixing // up this server, or fixing any of the "chess game" bugs. Any bugs // found in the socket classes I would be interested in, although they are // throughly tested. // // I built this server one night as a demo for a company in hopes // of getting a job there. Didn't know a damn thing about sockets // so I used, and built upon a blockingsocket class that was found // in Inside Visual C++ 5.0 by MS PRESS. // // There are no rules to either game, so players can move whatever // piece to whatever square. There isn't even any error checking, // and you can't backspace. This is a very limited server in that // respect. /* The layout of the server is: 1. Create an Accept thread to accept connections on port 23 (telnet) 2. Get a connection, create a new accept thread to be able to handle more connections. 3. Log this person in, getting user name. 4. Create a lobby of sorts where you can do simple chat, create a new game, join a game, or watch a game. 5. This lobby reads a line of input from the socket, and then checks what the command the user typed was via ParsCmd. 6. When the person creates a game, we add the game object to a pending list, and wait for someone to join the game. 7. When a person JOIN's a game, we check for the game name on the pending list, and assign that person as the second player on that game object. 8. When you WATCH a game, you are added to a list of watches on that game object. The game objects: The base class is the BoardGame class. Chess and Checkers games are derived from BoardGame. This is to make a consistant interface for adding players to a board game, and drawing the board. */ // // NOTE: // // There is probably a bit of an issue with people disconnecting, // and things not being cleaned up. I never bothered to finish that // off, as it wasn't important. // #include <assert.h> #include <memory.h> #include <process.h> /* _beginthread, _endthread */ #include <iostream.h> #include <winsock.h> #include <stdlib.h> #include <stdio.h> #include <string.h> //strstr #include <llist.h> #include "blockingsocket.h" #include "gameob.h" int repeat=1; #define ASSERT assert #define VERIFY assert #define BUFFERSIZE 1000 CLList m_games; //list of Chess Game Objects (NOTE: This isn't thread safe!) CLList m_pending; //list of games that are waiting for second player (NOTE: This isn't thread safe!) CLList m_connections; //list of all the people connected void log(const char* s) { printf("%s\n",s); OutputDebugString(s); OutputDebugString("\n"); } //This is the main game loop. It reads in users moves, and prints out the board //contents to all people in a single game. void GameLoop(void* pParam) { log("GameLoop start"); BoardGame* pgame=(BoardGame*)pParam; char* help= "Enter the column & row of the piece you are moving from & to\r\n" "in the format FFTT. (ie: moving whites queen pawn ahead one from \r\n" "column 3, row 6 to column 3, row 5 would be entered 3635.)\r\n"; char* entermove="What is your move? (?=help) "; char* whitesmove="It's whites move."; char* blacksmove="It's blacks move."; bool bAbort=false; bool bMadeMove=false; char inbuf[10]; try { do { //handle whites move pgame->Draw(); pgame->m_black->Print(whitesmove); bMadeMove=false; do { pgame->m_white->Write(entermove,strlen(entermove)); pgame->m_white->ReadLine(inbuf,sizeof inbuf,300); if(!stricmp(inbuf,"quit")) { bAbort=true; pgame->m_white->Print("Aborting game"); pgame->m_black->Print("White aborted game"); } else if(!strcmp(inbuf,"?")) { pgame->m_white->Print(help); } else { if(strlen(inbuf)!=4) pgame->m_white->Print("Syntax error"); else { if(pgame->ValidMove(inbuf[0],inbuf[1],inbuf[2],inbuf[3])) { bMadeMove=true; pgame->Move(inbuf[0],inbuf[1],inbuf[2],inbuf[3]); } else pgame->m_white->Print("not a valid move"); } } } while(!bMadeMove && !bAbort); //handle blacks move if(!bAbort) { pgame->Draw(); pgame->m_white->Print(blacksmove); do { pgame->m_black->Write(entermove,strlen(entermove)); pgame->m_black->ReadLine(inbuf,sizeof inbuf,300); if(!stricmp(inbuf,"quit")) { bAbort=true; pgame->m_black->Print("Aborting game"); pgame->m_white->Print("Black aborted game"); } else if(!strcmp(inbuf,"?")) { pgame->m_black->Print(help); } else { if(strlen(inbuf)!=4) pgame->m_white->Print("Syntax error"); else { bMadeMove=true; pgame->Move(inbuf[0],inbuf[1],inbuf[2],inbuf[3]); } } } while(!bMadeMove && !bAbort); } } while(!bAbort); } catch(const char* e) { log(e); } CLList* plist=m_games.FindItem(pgame); m_games.RemoveItem(plist); // delete pgame; log("GameLoop end"); } //read in the persons user name, and make sure they are a valid user bool Login(CTelnetSocket* psocket) { char* login="Enter your login name (only \"guest\" works): "; char line[1000]; psocket->Write(login,strlen(login)); psocket->ReadLine(line,sizeof line); if(stricmp(line,"guest")) return false; psocket->m_wFlags |= CTelnetSocket::FLAG_VALIDATED; return true; } ChessGameOb* FindGame(const char* pgamename) { log("Findgame"); ChessGameOb* o; CLList* plist=m_games.GetHead(); while(plist) { o=(ChessGameOb*)plist->GetItem(); log(o->m_gamename); if(!stricmp(o->m_gamename,pgamename)) { //found the game name return o; } plist=plist->GetNext(); } return NULL; } ChessGameOb* FindPendingGame(const char* pgamename) { log("Find pending game"); ChessGameOb* o; CLList* plist=m_pending.GetHead(); while(plist) { o=(ChessGameOb*)plist->GetItem(); log(o->m_gamename); if(!stricmp(o->m_gamename,pgamename)) { //found the game name return o; } plist=plist->GetNext(); } return NULL; } enum { CREATE=1, JOIN, WATCH, QUIT, PLAY, COLOR }; char* cmds[]={"create","join","watch","quit","play","color"}; int FindToken(const char* buf) { for(int i=0; i<sizeof cmds/sizeof cmds[0]; i++) { if(strstr(buf,cmds[i])) return i+1; } return 0; } //send the chat in buf to all connections void Chat(CPersonInstance* pInstance, char* buf) { CPersonInstance* o; CLList* plist=m_connections.GetHead(); while(plist) { o=(CPersonInstance*)plist->GetItem(); if(o!=pInstance) o->Print(buf); plist=plist->GetNext(); } } //Look at what the user typed, and act upon it. bool ParsCmd(char* buf,CPersonInstance* pInstance) { char* SyntaxErrorMsg="Syntax error. 'create chess|checkers name' " ; int itoken=FindToken(buf); char cmd[30],parm1[30],parm2[30]; BoardGame* pgame; CLList* plist; switch(itoken) { case WATCH: //user typed "watch game" sscanf(buf,"%s %s",cmd,parm1); pgame=FindGame(parm1); if(!pgame) pInstance->Print("No game found"); else { if(!pgame->HasAllPlayers()) pInstance->Print("Both players have to be playing befor watchin is aloud"); else { pgame->AddWatcher(pInstance); pInstance->Print("You will see the board when the next person moves"); } return true; } break; case CREATE: //user typed "create chess game" if(3!=sscanf(buf,"%s %s %s",cmd,parm1,parm2)) //get game name { pInstance->Print(SyntaxErrorMsg); } else { if(!stricmp(parm1,"checkers")) { pgame=new CheckerGameOb(pInstance,parm2); pInstance->Print("Created new checkers game"); } else if(!stricmp(parm1,"chess")) { pInstance->Print("Created new chess game"); pgame=new ChessGameOb(pInstance,parm2); } else { pInstance->Print(SyntaxErrorMsg); return false; } m_pending.AddTail(pgame); #if 1 log("Pending games:"); plist=m_pending.GetHead(); while(plist) { pgame=(BoardGame*)plist->GetItem(); log(pgame->m_gamename); plist=plist->GetNext(); } #endif pInstance->Print("You must now wait till someone joins your game"); return true; } break; case JOIN: //user typed "join game" sscanf(buf,"%s %s",cmd,parm1); pgame=FindPendingGame(parm1); if(pgame) { pgame->AddBlack(pInstance); plist=m_pending.FindItem(pgame); if(plist) m_pending.RemoveItem(plist); m_games.AddTail(pgame); _beginthread(GameLoop,0,pgame); return true; } else { pInstance->Print("No game found"); } break; case QUIT: //user typed "quit" pInstance->Print("Good bye"); pInstance->Close(); delete pInstance; return true; default: //LBP (01/08/99): Print this chat to all players in the lobby. Chat(pInstance,buf); break; } return false; } //LBP (01/08/99): this is the lobby of sorts. void TalkProc(void* pParam) { log("TalkProc start"); char* msg[]= { "Type 'create name' to make a game where 'name' is the game name you choose.\r\n" //0 "Type 'join name' to join a player that created a game.\r\n" "Type 'watch name' to watch a game in progress", "Good bye.", //1 "Syntax Error" //2 }; CPersonInstance* pInstance=(CPersonInstance*)pParam; char inbuf[CTelnetSocket::nSizeRecv]; bool bAbort=false; m_connections.AddTail(pInstance); try { pInstance->Print(msg[0]); do { pInstance->Write("% ",2); pInstance->ReadLine(inbuf,sizeof inbuf,1800); if(ParsCmd(inbuf,pInstance)) { bAbort=true; } } while(!bAbort); } catch(const char* e) { log(e); pInstance->Cleanup(); delete pInstance; } log("TalkProc end"); _endthread(); } void AcceptProc(void* pParam) { CBlockingSocket* psockListen=(CBlockingSocket*)pParam; CSockAddr saClient; CPersonInstance sConnect; char* buffer; try { char* welcome="Welcome to the club."; char* nonuser="Sorry, members only"; buffer=(char*)calloc(1,1000); assert(buffer); if(!psockListen->Accept(sConnect,saClient)) _endthread(); _beginthread(AcceptProc,0,pParam); //get ready for another connection log("Connection accepted"); if(!Login(&sConnect)) { sConnect.Print(nonuser); sConnect.Close(); } else { sConnect.Print(welcome); //LBP (01/08/99): start up a lobby for this new connection CPersonInstance* newinstance=new CPersonInstance(sConnect); _beginthread(TalkProc,0,newinstance); } } catch(const char* e) { log(e); sConnect.Cleanup(); } _endthread(); } void CheckKey(void* dummy) { int a; cin >> a; repeat=0; } void ProcessGames() { _beginthread(CheckKey,0,NULL); do { } while(repeat); } void main(void) { CBlockingSocket sockListen; WSADATA wsd; if(WSAStartup(0x0101,&wsd)!=0) { cout << "Unable to start socket\n"; } else { cout << "Chess Server Demo by Lee Patterson\n"; cout << "leepatterson@home.com\n\n"; cout << "Demonstrates an object oriented server that can handle\n"; cout << "any number of chess games with 2 players and any number of\n"; cout << "watchers per game.\n\n"; cout << "Use telnet as a client connect to this server (ie: telnet localhost).\n"; cout << "Server uses port 23 (telnet default).\n"; cout << "Ctrl+c exits server"; try { CSockAddr saServer(INADDR_ANY,23); sockListen.Create(); sockListen.Bind(saServer); sockListen.Listen(); _beginthread(AcceptProc,0,&sockListen); } catch(const char* e) { sockListen.Cleanup(); cout << e << "\n"; } ProcessGames(); try { sockListen.Close(); Sleep(300); WSACleanup(); } catch(const char* e) { cout << e << "\n"; } } }